From c804bd9153d9e5ad304ce2aef4bb0e9c15efe60a Mon Sep 17 00:00:00 2001 From: =?utf8?q?=C3=98yvind=20Kol=C3=A5s?= Date: Sun, 20 Aug 2017 22:39:04 +0200 Subject: [PATCH] tools: add babl-icc-dump This commandline tool contains code to parse the v4 elements babl-icc currently doesn't make use of. It loads data from the ICC profile directly into double precision floats. CIE xy red: 0.640009 0.330022 CIE xy green: 0.300008 0.600015 CIE xy blue: 0.150011 0.059998 Is due to IEEE double precision floats not being as precise as the fixed point values in the profile, which are *exactly*: CIE xy red: 0.6400 0.3300 CIE xy green: 0.3000 0.6000 CIE xy blue: 0.1500 0.0600 --- babl/babl-icc.c | 34 ++- tools/Makefile.am | 2 +- tools/babl-benchmark.c | 18 +- tools/babl-icc-dump.c | 460 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 484 insertions(+), 30 deletions(-) create mode 100644 tools/babl-icc-dump.c diff --git a/babl/babl-icc.c b/babl/babl-icc.c index 8521fce..7277f67 100644 --- a/babl/babl-icc.c +++ b/babl/babl-icc.c @@ -45,8 +45,8 @@ static int load_sbyte (const char *icc, int length, int offset) static int16_t load_u1f15 (const char *icc, int length, int offset) { - return load_sbyte (icc, length, offset + 1) + - (load_byte (icc, length, offset + 0) << 8); + return load_byte (icc, length, offset + 1) + + (load_sbyte (icc, length, offset + 0) << 8); } static uint16_t load_u16 (const char *icc, int length, int offset) @@ -61,12 +61,6 @@ static double load_s15f16 (const char *icc, int length, int offset) load_u16 (icc, length, offset + 2) / 65535.0f; } -static double load_u16f16 (const char *icc, int length, int offset) -{ - return load_u16 (icc, length, offset) + - load_u16 (icc, length, offset + 2) / 65535.0; -} - static uint32_t load_u32 (const char *icc, int length, int offset) { return load_byte (icc, length, offset + 3) + @@ -238,9 +232,9 @@ babl_space_rgb_icc (const char *icc, icc_tag (icc, length, "wtpt", &offset, &element_size); { - double wX = load_u16f16 (icc, length, offset + 8); - double wY = load_u16f16 (icc, length, offset + 8 + 4); - double wZ = load_u16f16 (icc, length, offset + 8 + 4 * 2); + double wX = load_s15f16 (icc, length, offset + 8); + double wY = load_s15f16 (icc, length, offset + 8 + 4); + double wZ = load_s15f16 (icc, length, offset + 8 + 4 * 2); return babl_space_rgb_chromaticities (NULL, wX / (wX + wY + wZ), @@ -262,17 +256,17 @@ babl_space_rgb_icc (const char *icc, double rz, gz, bz; icc_tag (icc, length, "rXYZ", &offset, &element_size); - rx = load_u16f16 (icc, length, offset + 8 + 4 * 0); - ry = load_u16f16 (icc, length, offset + 8 + 4 * 1); - rz = load_u16f16 (icc, length, offset + 8 + 4 * 2); + rx = load_s15f16 (icc, length, offset + 8 + 4 * 0); + ry = load_s15f16 (icc, length, offset + 8 + 4 * 1); + rz = load_s15f16 (icc, length, offset + 8 + 4 * 2); icc_tag (icc, length, "gXYZ", &offset, &element_size); - gx = load_u16f16 (icc, length, offset + 8 + 4 * 0); - gy = load_u16f16 (icc, length, offset + 8 + 4 * 1); - gz = load_u16f16 (icc, length, offset + 8 + 4 * 2); + gx = load_s15f16 (icc, length, offset + 8 + 4 * 0); + gy = load_s15f16 (icc, length, offset + 8 + 4 * 1); + gz = load_s15f16 (icc, length, offset + 8 + 4 * 2); icc_tag (icc, length, "bXYZ", &offset, &element_size); - bx = load_u16f16 (icc, length, offset + 8 + 4 * 0); - by = load_u16f16 (icc, length, offset + 8 + 4 * 1); - bz = load_u16f16 (icc, length, offset + 8 + 4 * 2); + bx = load_s15f16 (icc, length, offset + 8 + 4 * 0); + by = load_s15f16 (icc, length, offset + 8 + 4 * 1); + bz = load_s15f16 (icc, length, offset + 8 + 4 * 2); return babl_space_rgb_matrix (NULL, rx, gx, bx, diff --git a/tools/Makefile.am b/tools/Makefile.am index 3fb8fd5..3cde051 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -3,7 +3,7 @@ AM_CPPFLAGS = -I$(top_srcdir) -I$(top_srcdir)/babl LDADD = $(top_builddir)/babl/libbabl-@BABL_API_VERSION@.la \ $(MATH_LIB) $(THREAD_LIB) -noinst_PROGRAMS = babl-verify babl-benchmark +noinst_PROGRAMS = babl-verify babl-benchmark babl-icc-dump if HAVE_SRANDOM noinst_PROGRAMS += \ babl-gen-test-pixels diff --git a/tools/babl-benchmark.c b/tools/babl-benchmark.c index a887f5c..3ed3539 100644 --- a/tools/babl-benchmark.c +++ b/tools/babl-benchmark.c @@ -74,15 +74,15 @@ test (void) babl_format_with_space("RGBA float", babl_space("ProPhoto")), babl_format_with_space("R'G'B' u16", babl_space("ProPhoto")), #endif - babl_format("RGBA float"), - babl_format("R'G'B'A float"), - babl_format("R'G'B' u8"), babl_format("CIE Lab float"), - babl_format_with_space("R'G'B' u8", babl_space("Adobe")), - babl_format_with_space("R'G'B' u8", babl_space("ProPhoto")), - babl_format_with_space("Y float", babl_space("ProPhoto")), + babl_format("RGBA float"), + babl_format("Y float"), + babl_format("R'G'B'A u8"), + babl_format_with_space("RGBA float", babl_space("ProPhoto")), babl_format_with_space("R'G'B'A float", babl_space("ProPhoto")), - babl_format_with_space("RGBA float", babl_space("ProPhoto")) + babl_format_with_space("Y float", babl_space("ProPhoto")), + babl_format_with_space("R'G'B'A u8", babl_space("Adobe")), + babl_format_with_space("R'G'B'A u8", babl_space("ProPhoto")), }; int n_formats = sizeof (formats) / sizeof (formats[0]); double mbps[50 * 50] = {0,}; @@ -105,8 +105,8 @@ test (void) long end, start; int iters = ITERATIONS; - fprintf (stderr, "%s to %s\r", babl_get_name (formats[i]), - babl_get_name (formats[j])); + fprintf (stderr, "%s to %s \r", babl_get_name (formats[i]), + babl_get_name (formats[j])); fflush (0); /* a quarter round of warmup */ diff --git a/tools/babl-icc-dump.c b/tools/babl-icc-dump.c new file mode 100644 index 0000000..6adb8ff --- /dev/null +++ b/tools/babl-icc-dump.c @@ -0,0 +1,460 @@ +#include +#include +#include "../config.h" +#include "babl/babl-internal.h" + +static int +file_get_contents (const char *path, + char **contents, + long *length, + void *error); + + +#define ICC_HEADER_LEN 128 +#define TAG_COUNT_OFF ICC_HEADER_LEN + +static int load_byte (const char *icc, int length, int offset) +{ +/* all reading functions take both the char *pointer and the length of the + * buffer, and all reads thus gets protected by this condition. + */ + if (offset < 0 || offset > length) + return 0; + + return *(uint8_t*) (&icc[offset]); +} + +static int load_sbyte (const char *icc, int length, int offset) +{ + if (offset < 0 || offset > length) + return 0; + + return *(int8_t*) (&icc[offset]); +} + +static int16_t load_u1f15 (const char *icc, int length, int offset) +{ + return load_byte (icc, length, offset + 1) + + (load_sbyte (icc, length, offset + 0) << 8); +} + +static uint16_t load_u16 (const char *icc, int length, int offset) +{ + return load_byte (icc, length, offset + 1) + + (load_byte (icc, length, offset + 0) << 8); +} + +static double load_s15f16 (const char *icc, int length, int offset) +{ + return load_u1f15 (icc, length, offset) + + load_u16 (icc, length, offset + 2) / 65535.0f; +} + +static uint32_t load_u32 (const char *icc, int length, int offset) +{ + return load_byte (icc, length, offset + 3) + + (load_byte (icc, length, offset + 2) << 8) + + (load_byte (icc, length, offset + 1) << 16) + + (load_byte (icc, length, offset + 0) << 24); +} + +static void load_sign (const char *icc, int length, + int offset, char *sign) +{ + sign[0]=load_byte(icc, length, offset); + sign[1]=load_byte(icc, length, offset + 1); + sign[2]=load_byte(icc, length, offset + 2); + sign[3]=load_byte(icc, length, offset + 3); + sign[4]=0; +} + +/* looks up offset and length for a specific icc tag + */ +static int icc_tag (const char *icc, int length, + const char *tag, int *offset, int *el_length) +{ + int tag_count = load_u32 (icc, length, TAG_COUNT_OFF); + int t; + + for (t = 0; t < tag_count; t++) + { + char tag_signature[5]; + load_sign (icc, length, TAG_COUNT_OFF + 4 + 12 * t, tag_signature); + if (!strcmp (tag_signature, tag)) + { + if (offset) + *offset = load_u32 (icc, length, TAG_COUNT_OFF + 4 + 12* t + 4); + if (el_length) + *el_length = load_u32 (icc, length, TAG_COUNT_OFF + 4 + 12* t + 4*2); + return 1; + } + } + return 0; +} + +#if 0 + +#define ICC_HEADER_LEN 128 +#define TAG_COUNT_OFF ICC_HEADER_LEN + +static int load_byte (const char *icc, int offset) +{ + return *(uint8_t*) (&icc[offset]); +} + +static int16_t load_u1Fixed15 (const char *icc, int offset) +{ + return load_byte (icc, offset + 1) + + (load_byte (icc, offset + 0) << 8); +} + +static uint16_t load_u16 (const char *icc, int offset) +{ + return load_byte (icc, offset + 1) + + (load_byte (icc, offset + 0) << 8); +} + +static double load_s15f16 (const char *icc, int offset) +{ + return load_u1Fixed15 (icc, offset) + load_u16 (icc, offset + 2) / 65535.0f; +} + +static double load_u16f16 (const char *icc, int offset) +{ + return load_u16 (icc, offset) + load_u16 (icc, offset + 2) / 65535.0; +} + +static uint32_t load_u32 (const char *icc, int offset) +{ + return load_byte (icc, offset + 3) + + (load_byte (icc, offset + 2) << 8) + + (load_byte (icc, offset + 1) << 16) + + (load_byte (icc, offset + 0) << 24); +} + +static float load_float32 (const char *icc, int offset) +{ + char buf[4]={load_byte (icc, offset + 3), + load_byte (icc, offset + 2), + load_byte (icc, offset + 1), + load_byte (icc, offset + 0)}; + float *val = (float*)(&buf[0]); + return *val; +} + +static uint64_t load_uint64 (const char *icc, int offset) +{ + return ((uint64_t)load_byte (icc, offset + 7) << (8*0)) + + ((uint64_t)load_byte (icc, offset + 6) << (8*1)) + + ((uint64_t)load_byte (icc, offset + 5) << (8*2)) + + ((uint64_t)load_byte (icc, offset + 4) << (8*3)) + + ((uint64_t)load_byte (icc, offset + 3) << (8*4)) + + ((uint64_t)load_byte (icc, offset + 2) << (8*5)) + + ((uint64_t)load_byte (icc, offset + 1) << (8*6)) + + ((uint64_t)load_byte (icc, offset + 0) << (8*7)); +} + +static void load_sign (const char *icc, int offset, char *sign) +{ + sign[0]=load_byte(icc, offset); + sign[1]=load_byte(icc, offset + 1); + sign[2]=load_byte(icc, offset + 2); + sign[3]=load_byte(icc, offset + 3); + sign[4]=0; +} + +static int icc_tag (const char *icc, const char *tag, int *offset, int *length) +{ + int tag_count = load_u32 (icc, TAG_COUNT_OFF); + int profile_size = load_u32 (icc, 0); + int t; + + for (t = 0; t < tag_count; t++) + { + char tag_signature[5]; + load_sign (icc, TAG_COUNT_OFF + 4 + 12 * t, tag_signature); + if (!strcmp (tag_signature, tag)) + { + *offset = load_u32 (icc, TAG_COUNT_OFF + 4 + 12* t + 4); + *length = load_u32 (icc, TAG_COUNT_OFF + 4 + 12* t + 4 * 2); + /* avert potential for maliciousnes.. */ + if (*offset >= profile_size) + { + *offset = profile_size - 1; + } + if (*offset + *length >= profile_size) + { + *length = profile_size - *offset - 1; + } + return 1; + } + } + return 0; +} +#endif + +static int load_icc_from_memory (const char *icc, long length, char **error) +{ + int tag_count = load_u32 (icc, length, TAG_COUNT_OFF); + int profile_size = load_u32 (icc, length, 0); + int profile_version_major = load_byte (icc, length, 8); + int profile_version_minor = load_byte (icc, length, 9) >> 4; + int profile_version_micro = load_byte (icc, length, 9) & 0xf; + char profile_class[5]; + char color_space[5]; + char pcs_space[5]; + int rendering_intent = load_u32 (icc, length, 64); + int t; + // 64..67 rendering intent + // 68..79 XYZ of D50 + + load_sign (icc, length, 16, color_space); + load_sign (icc, length, 20, pcs_space); + load_sign (icc, length, 12, profile_class); + + if (strcmp (profile_class, "mntr")) + { + *error = "not a monitor-class profile"; + return -1; + } + if (strcmp (color_space, "RGB ")) + { + *error = "not defining an RGB space"; + return -1; + } +#if 0 + if (profile_version_major > 2) + { + *error = "only ICC v2 profiles supported"; + return -1; + } +#endif + { + int offset, element_size; + icc_tag (icc, length, "desc", &offset, &element_size); + fprintf (stdout, "desc: %s\n", icc + offset + 12); + } + { + int offset, element_size; + icc_tag (icc, length, "cprt", &offset, &element_size); + fprintf (stdout, "copyright: %s\n", icc + offset + 8); + } + +#if 1 + fprintf (stdout, "icc version: %i.%i.%i\n", profile_version_major, profile_version_minor, profile_version_micro); + fprintf (stdout, "profile-size: %i\n", profile_size); + fprintf (stdout, "profile-class: %s\n", profile_class); + fprintf (stdout, "color-space: %s\n", color_space); + fprintf (stdout, "rendering-intent: %i\n", rendering_intent); + fprintf (stdout, "pcs-space: %s\n", pcs_space); + fprintf (stdout, "byte length: %li\n", length); + fprintf (stdout, "tag-count: %i\n", tag_count); + + for (t = 0; t < tag_count; t++) + { + char tag_signature[5]; + int offset, element_size; + load_sign (icc, length, TAG_COUNT_OFF + 4 + 12 * t, tag_signature); + icc_tag (icc, length, tag_signature, &offset, &element_size); + fprintf (stdout, "tag %i %s %i %i\n", t, tag_signature, offset, element_size); + } +#endif + fprintf (stdout, "tags: "); + for (t = 0; t < tag_count; t++) + { + char tag_signature[5]; + int offset, element_size; + load_sign (icc, length, TAG_COUNT_OFF + 4 + 12 * t, tag_signature); + icc_tag (icc, length, tag_signature, &offset, &element_size); + fprintf (stdout, "%s ", tag_signature); + } + fprintf (stdout, "\n"); + + { + int offset, element_size; + if (icc_tag (icc, length, "chrm", &offset, &element_size)) + { + int channels = load_u16 (icc, length, offset + 8); + int phosporant = load_u16 (icc, length, offset + 10); + double redX = load_s15f16 (icc, length, offset + 12); + double redY = load_s15f16 (icc, length, offset + 12 + 4); + double greenX = load_s15f16 (icc, length, offset + 20); + double greenY = load_s15f16 (icc, length, offset + 20 + 4); + double blueX = load_s15f16 (icc, length, offset + 28); + double blueY = load_s15f16 (icc, length, offset + 28 + 4); + fprintf (stdout, "chromaticity:\n"); + fprintf (stdout, " channels: %i\n", channels); + fprintf (stdout, " phosphorant: %i\n", phosporant); + fprintf (stdout, " CIE xy red: %f %f\n", redX, redY); + fprintf (stdout, " CIE xy green: %f %f\n", greenX, greenY); + fprintf (stdout, " CIE xy blue: %f %f\n", blueX, blueY); + } + } + + { + int offset, element_size; + if (icc_tag (icc, length, "wtpt", &offset, &element_size)) + { + double wX = load_s15f16 (icc, length, offset + 8); + double wY = load_s15f16 (icc, length, offset + 8 + 4); + double wZ = load_s15f16 (icc, length, offset + 8 + 4 * 2); + fprintf (stdout, "whitepoint CIE xyz: %f %f %f\n", wX, wY, wZ); + } + } + + { + int offset, element_size; + if (icc_tag (icc, length, "rXYZ", &offset, &element_size)) + { + double wX = load_s15f16 (icc, length, offset + 8); + double wY = load_s15f16 (icc, length, offset + 8 + 4); + double wZ = load_s15f16 (icc, length, offset + 8 + 4 * 2); + fprintf (stdout, "Red CIE xyz: %f %f %f\n", wX, wY, wZ); + } + } + { + int offset, element_size; + if (icc_tag (icc, length, "gXYZ", &offset, &element_size)) + { + double wX = load_s15f16 (icc, length, offset + 8); + double wY = load_s15f16 (icc, length, offset + 8 + 4); + double wZ = load_s15f16 (icc, length, offset + 8 + 4 * 2); + fprintf (stdout, "Green CIE xyz: %f %f %f\n", wX, wY, wZ); + } + } + { + int offset, element_size; + if (icc_tag (icc, length, "bXYZ", &offset, &element_size)) + { + double wX = load_s15f16 (icc, length, offset + 8); + double wY = load_s15f16 (icc, length, offset + 8 + 4); + double wZ = load_s15f16 (icc, length, offset + 8 + 4 * 2); + fprintf (stdout, "Blue CIE xyz: %f %f %f\n", wX, wY, wZ); + } + } + + if (1) { + int offset, element_size; + if (icc_tag (icc, length, "rTRC", &offset, &element_size)) + { + int count = load_u32 (icc, length, offset + 8); + int i; + if (!strcmp (icc + offset, "para")) + { + int function_type = load_u16 (icc, length, offset + 8); + float g,a,b,c,d,e,f; + switch (function_type) + { + case 0: + g = load_s15f16 (icc, length, offset + 12 + 2 * 0); + fprintf (stdout, "parametric TRC gamma type %f\n", g); + break; + + case 3: + g = load_s15f16 (icc, length, offset + 12 + 2 * 0); + a = load_s15f16 (icc, length, offset + 12 + 2 * 1); + b = load_s15f16 (icc, length, offset + 12 + 2 * 2); + c = load_s15f16 (icc, length, offset + 12 + 2 * 3); + d = load_s15f16 (icc, length, offset + 12 + 2 * 4); + e = load_s15f16 (icc, length, offset + 12 + 2 * 5); + f = load_s15f16 (icc, length, offset + 12 + 2 * 5); + fprintf (stdout, "parametric TRC sRGB type %f %f %f %f %f %f %f\n", g, a, b, c, d, e, f); + break; + default: + fprintf (stdout, "unhandled parametric TRC type %i\n", function_type); + break; + } + } + else + { + fprintf (stdout, "rTRC count: %i %s\n", count, icc + offset); + if (count == 0) + { + fprintf (stdout, "linear TRC\n"); + } + else if (count == 1) + { + fprintf (stdout, "gamma TRC of: %f\n", load_byte (icc, length, offset + 12) + + load_byte (icc, length, offset + 12 + 1) / 255.0); + } + else for (i = 0; i < count && i < 10; i ++) + { + fprintf (stdout, "%i=%i ", i, load_u16 (icc, length, offset + 12 + i * 2)); + if (i % 7 == 0) + fprintf (stdout, "\n"); + } + } + } + } + return 0; +} + +static int load_icc (const char *path, char **error) +{ + char *icc = NULL; + long length = 0; + int ret = 0; + file_get_contents (path, &icc, &length, NULL); + if (icc) + { + ret = load_icc_from_memory (icc, length, error); + free (icc); + } + return ret; +} + +static int +file_get_contents (const char *path, + char **contents, + long *length, + void *error) +{ + FILE *file; + long size; + char *buffer; + + file = fopen (path,"rb"); + + if (!file) + return -1; + + fseek (file, 0, SEEK_END); + *length = size = ftell (file); + rewind (file); + buffer = malloc(size + 8); + + if (!buffer) + { + fclose(file); + return -1; + } + + size -= fread (buffer, 1, size, file); + if (size) + { + fclose (file); + free (buffer); + return -1; + } + fclose (file); + *contents = buffer; + return 0; +} + +int main (int argc, char **argv) +{ + char *error = NULL; + if (argc < 2) + { + fprintf (stdout, "need one arg, an ICC profile file\n"); + return -1; + } + + load_icc (argv[1], &error); + if (error) + { + fprintf (stdout, "icc-parse-problem: %s\n", error); + } + + return 0; +} -- 2.30.2